import ij.ImageStack;

import java.awt.Color;
import java.util.ArrayList;

import cells.AbstractVoxelCell;

/**
 * The Purpose of this class is to store the ImageData
 * 
 * 
 * @author Stefan Fuchs
 *
 */
public class Modell {
	
	////////////////////////////////////////////////////////
	//////////////////// Constants /////////////////////////
	////////////////////////////////////////////////////////
	
	/** Label color for internal points */
	public static final Color INTERNAL_POINTS_LABEL = Color.RED;

	/** Label color for external points */
	public static final Color EXTERNAL_POINTS_LABEL = Color.GREEN;
	
	/** Label color for points of ventricle a*/
	public static final Color VENTRICLE_A_POINTS_LABEL = Color.CYAN;
	
	/** Label color for points of ventricle b*/
	public static final Color VENTRICLE_B_POINTS_LABEL = Color.MAGENTA;
	
	/** Label color for overlapping mycard from left and right ventricle*/
	public static final Color OVERLAPPING_MYOCARD_LABEL = Color.ORANGE;
	
	/** Label color for manually added voxels after ASF*/
	public static final Color MANUALLY_ADDED_VOXELS = Color.BLUE;
	
	/** Label color for manually deleted voxels after ASF*/
	public static final Color MANUALLY_DELETED_VOXELS = Color.PINK;
	
	/** Label color for manually corrected voxels*/
	public static final Color MANUALLY_CORRECTED_VOXELS = Color.GREEN;
	
	/** Color of AV-Node voxels*/
	public static final Color AV_NODE_VOXELS = Color.RED;
	
	/** Color of the Conductor cells*/
	public static final Color COLOR_CONDUCTOR_CELLS = Color.RED;
	
	/** Color of Myocard cells */
	public static final Color COLOR_MYOCARD_CELLS = Color.CYAN;
	
	
	////////////////////////////////////////////////////////
	//////////////////// Variables /////////////////////////
	////////////////////////////////////////////////////////
	
		/////////////////// int ///////////////////////////

	/** Width from current image. */
	private int width;

	/** Height from current image. */
	private int height;

	/** Slices from current image. */
	private int size;
	
	/** Minimal roi parameter, X-axis. */
	private int minRoiX;

	/** Maximal roi parameter, X-axis. */
	private int maxRoiX;

	/** Minimal roi parameter, Y-axis. */
	private int minRoiY;

	/** Maximal roi parameter, Y-axis. */
	private int maxRoiY;

	/** Minimal roi parameter, Z-axis. */
	private int minRoiZ;

	/** Maximal roi parameter, Z-axis */
	private int maxRoiZ;
	
		////////// Image J Objects //////////////////////////

	/** The ImageStack */
	private ImageStack imageStack = null;

	/** The ImageStack as 8-bit grey value image*/
	private ImageStack grayValueImageStack = null;
	
		/////////// Heart specific //////////////////////////
	
	/** Width of one voxel in mm (x-direction)*/
	private double voxelWidth;
	
	/** Height of one voxel in mm (y-direction)*/
	private double voxelHeight;
	
	/** Depth of one voxel in mm (z-direction)*/
	private double voxelDepth;
	
	////////////////////////////////////////////////////////
	////////////////////// Results /////////////////////////
	////////////////////////////////////////////////////////
	
		//after drawing borders and roi
	
	/** {@link ArrayList} of the external Border {@link Voxel}s */
	private ArrayList<Voxel> internalVoxels = null;

	/** {@link ArrayList} of internal {@link Voxel}s */
	private ArrayList<Voxel> externalVoxels = null;
	
		//after CASegmentation3D 
	
	/** The connected components label */
	private int[] connectedComponentLabel = null;
	
		//afer Alternating-Sequnce-Filtering
	
	/** Alternating-Sequence-Filtering label */
	private int[] asfLabel = null;
	
		//after Rolling-Ball
	/** Label with the myocardium */
	private int[] myocardiumLabel = null;
	
		//after drawing the AV-node
	/** {@link ArrayList} of voxels that contain the AV-node*/
	private ArrayList<Voxel> avNodeVoxels = null;
	
		//The final Arraylist
	private ArrayList<AbstractVoxelCell> voxelCells = null;
	
	////////////////////////////////////////////////////////
	////////////////// Singletonpart ///////////////////////
	////////////////////////////////////////////////////////
	
	private static Modell instance = null;
	
	public static synchronized Modell getInstance() {
		
		if ( instance == null ) {
			instance = new Modell();
		}
		
		return instance;
	}
	
	private Modell() {
		//dummy constructor
	}
	
	////////////////////////////////////////////////////////
	///////////// END Singletonpart ////////////////////////
	////////////////////////////////////////////////////////
	
	
	////////////////////////////////////////////////////////
	////////////////// Setters & Getters ///////////////////
	////////////////////////////////////////////////////////
	
	/**
	 * Sets the dimensions of the image
	 */
	public void setDimension( int width, int height, int size ) {
		this.width = width;
		this.height = height;
		this.size = size;
	}
	
	/**
	 * Returns the width (x-wise)
	 * 
	 * @return
	 * 		width
	 */
	public int getWidth() {
		return width;
	}
	
	/**
	 * Returns the height (y-wise)
	 * 
	 * @return
	 * 		height
	 */
	public int getHeight() {
		return height;
	}
	
	/**
	 * Returns the size (z-wise)
	 * 
	 * @return
	 * 		size
	 */
	public int getSize() {
		return size;
	}
	
	/**
	 * Set the Roi Parameters
	 * 
	 * @param minRoiX
	 *            Minimum X-Axis value of ROI
	 * @param maxRoiX
	 *            Maximum X-Axis value of ROI
	 * @param minRoiY
	 *            Minimum Y-Axis value of ROI
	 * @param maxRoiY
	 *            Maximum Y-Axis value of ROI
	 * @param minRoiZ
	 *            Minimum Z-Axis value of ROI
	 * @param maxRoiZ
	 *            Maximum Z-Axis value of ROI
	 */
	public void setRoiParameters(int minRoiX, int maxRoiX, int minRoiY,
			int maxRoiY, int minRoiZ, int maxRoiZ) {
		this.minRoiX = minRoiX;
		this.maxRoiX = maxRoiX;
		this.minRoiY = minRoiY;
		this.maxRoiY = maxRoiY;
		this.minRoiZ = minRoiZ;
		this.maxRoiZ = maxRoiZ;
	}
	
	/**
	 * Returns the minimum ROI x-coordinate
	 * 
	 * @return
	 * 		min ROI x
	 */
	public int getMinRoiX() {
		return minRoiX;
	}
	
	/**
	 * Returns the maximum ROI x-coordinate
	 * 
	 * @return
	 * 		max ROI x
	 */
	public int getMaxRoiX() {
		return maxRoiX;
	}

	/**
	 * Returns the minimum ROI y-coordinate
	 * 
	 * @return
	 * 		min ROI y
	 */
	public int getMinRoiY() {
		return minRoiY;
	}
	
	/**
	 * Returns the maximum ROI y-coordinate
	 * 
	 * @return
	 * 		max ROI y
	 */
	public int getMaxRoiY() {
		return maxRoiY;
	}
	
	/**
	 * Returns the minimum ROI z-coordinate
	 * 
	 * @return
	 * 		min ROI z
	 */
	public int getMinRoiZ() {
		return minRoiZ;
	}
	
	/**
	 * Returns the maximum ROI z-coordinate
	 * 
	 * @return
	 * 		max ROI z
	 */
	public int getMaxRoiZ() {
		return maxRoiZ;
	}
	
	/**
	 * Sets the internal VoxelList
	 * 
	 * @param internalVoxels
	 */
	public void setInternalVoxels(ArrayList<Voxel> internalVoxels) {
		this.internalVoxels = internalVoxels;
	}
	
	/**
	 * Returns a copy of the internal VoxelList
	 * 
	 * @return
	 */
	public ArrayList<Voxel> getInternalVoxels() {
		return Util.copyVoxelList(this.internalVoxels);
	}
	
	/**
	 * Sets the external VoxelList
	 * 
	 * @param externalVoxels
	 */
	public void setExternalVoxels(ArrayList<Voxel> externalVoxels) {
		this.externalVoxels = externalVoxels;
	}
	
	/**
	 * Returns a copy of the external VoxelList
	 * 
	 * @return
	 */
	public ArrayList<Voxel> getExternalVoxels() {
		return Util.copyVoxelList(this.externalVoxels);
	}
	
	/**
	 * Sets the 1D-Array of the connected-component-label
	 * 
	 * @param connectedComponentLabel
	 */
	public void setConnectedComponentLabel(int[] connectedComponentLabel) {
		this.connectedComponentLabel = connectedComponentLabel;
	}
	
	/**
	 * Returns a copy of the connected-component-label
	 * 
	 * @return
	 */
	public int[] getConnectedComponentLabel() {
		return Util.copy1DVoxelArry(this.connectedComponentLabel);
	}
	
	/**
	 * Sets the Alternating-Sequence-Filtering Label
	 * 
	 * @param asfLabel
	 */
	public void setAlternatingSequenceFilteringLabel(int[] asfLabel) {
		this.asfLabel = asfLabel;
	}
	
	/**
	 * Return a copy of the Alternating-Sequence-Filtering Label
	 * 
	 * @return
	 * 		if the asfLabel is null the connectedComponentsLabel will be returned
	 */
	public int[] getAlternatingSequenceFilteringLabel() {
		if ( this.asfLabel == null ) {
			return Util.copy1DVoxelArry(this.connectedComponentLabel);
		} else {
			return Util.copy1DVoxelArry(this.asfLabel);
		}
	}
	
	/**
	 * Sets the Myocardium Label
	 * 
	 * @param myocardiumLabel
	 */
	public void setMyocardiumLabel(int[] myocardiumLabel) {
		this.myocardiumLabel = myocardiumLabel;
	}
	
	/**
	 * Returns a copy of the Myocardium Label
	 * 
	 * @return
	 */
	public int[] getMyocardiumLabel() {
		return Util.copy1DVoxelArry(this.myocardiumLabel);
	}
	
	
	/**
	 * Sets the Imagestack
	 */
	public void setImageStack(ImageStack imageStack) {
		this.imageStack = imageStack;
	}
	
	/**
	 * Returns a copy of the Imagestack
	 * 
	 * @return
	 * 		Copy of the Imagestack		
	 */
	public ImageStack getImageStack() {
		return Util.copyStack(this.imageStack);
	}
	
	/**
	 * Sets the gray value image stack
	 * 
	 * @param grayValueImageStack
	 */
	public void setGrayValueImageStack(ImageStack grayValueImageStack) {
		this.grayValueImageStack = grayValueImageStack;
	}
	
	/**
	 * Returns a copy of the 8 bit grayvalue Imagestack
	 * 
	 * @return
	 */
	public ImageStack getGrayValueImageStack() {
		return Util.copyStack(this.grayValueImageStack);
	}
	
	/**
	 * Set the voxel width in mm/voxel (x-direction)
	 * 
	 * @param voxelWidth
	 */
	public void setVoxelWidth(double voxelWidth) {
		//TODO: delete and use correct values
		this.voxelWidth = 1;
//		this.voxelWidth = voxelWidth;
	}
	
	/**
	 * Get the voxel width in mm/voxel (x-direction)
	 * 
	 * @return
	 */
	public double getVoxelWidth() {
		return voxelWidth;
	}
	
	/**
	 * Set the voxel height in mm/voxel (y-direction)
	 * 
	 * @param voxelHeight
	 */
	public void setVoxelHeight(double voxelHeight) {
		//TODO: delete and use correct values
		this.voxelHeight = 1;
//		this.voxelHeight = voxelHeight;
	}

	/**
	 * Get the voxel height in mm/voxel (y-direction)
	 * 
	 * @return
	 */
	public double getVoxelHeight() {
		return voxelHeight;
	}
	
	/**
	 * Set the voxel depth in mm/voxel (z-direction)
	 * 
	 * @param voxelDepth
	 */
	public void setVoxelDepth(double voxelDepth) {
		//TODO: delete and use correct values
		this.voxelDepth = 1;
		//this.voxelDepth = voxelDepth;
	}

	/**
	 * Get the voxel depth in mm/voxel (z-direction)
	 * 
	 * @return
	 */
	public double getVoxelDepth() {
		return voxelDepth;
	}
	
	/**
	 * Sets the av-Node voxels
	 * 
	 * @param avNodeVoxels
	 */
	public void setAvNodeVoxels(ArrayList<Voxel> avNodeVoxels) {
		this.avNodeVoxels = avNodeVoxels; 
	}
	
	/**
	 * Returns a copy of the Arraylist of av-Node voxels
	 * 
	 * @return
	 */
	public ArrayList<Voxel> getAvNodeVoxels() {
		return Util.copyVoxelList(this.avNodeVoxels);
	}
	
	/**
	 * Sets the Voxel cells needed for the CA
	 * 
	 * @param voxelCells
	 */
	public void setVoxelCells(ArrayList<AbstractVoxelCell> voxelCells) {
		this.voxelCells = voxelCells;
	}
	
	/**
	 * returns the Voxel cells needed for the CA
	 * 
	 * @return
	 */
	public ArrayList<AbstractVoxelCell> getVoxelCells() {
		return voxelCells;
	}
}